import os
import random
import json
import urllib.parse
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from tqdm import tqdm
from comment_db import CommentDB, Platform
from BaseBot import BaseBot

limitation = 100


# 模拟自动评论的主类
class ZhihuBot(BaseBot):
    def __init__(self):
        base_dir = os.path.dirname(os.path.abspath(__file__))
        log_dir = os.path.join(base_dir, "log")
        data_dir = os.path.join(base_dir, "data")
        if not os.path.exists(data_dir):
            os.makedirs(data_dir)  # 确保data目录存在
        comment_path = os.path.join(data_dir, "comments.json")
        home_url = "https://www.zhihu.com"
        super().__init__(log_dir, comment_path, home_url)

        self.driver = None
        self.failed_comment_count = 0
        self.comment_db = CommentDB()

        self.cookie_path = os.path.join(data_dir, f"{self.class_name}_cookies.json")
        self.cache_path = os.path.join(data_dir, f"{self.class_name}_cached_hrefs.json")

    def remove_non_bmp(self, text):
        # return "".join(c for c in text if ord(c) <= 0xFFFF)
        return text

    def setup_browser(self):
        # options = Options()
        # options.add_argument("--start-maximized")
        chrome_options = Options()
        # chrome_options.add_argument('--headless')
        chrome_options.add_argument("--disable-gpu")
        chrome_options.add_argument("--no-sandbox")
        # driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
        self.driver = webdriver.Chrome(options=chrome_options)
        # self.driver = webdriver.Chrome(options=options)

    def setup_browser(self):
        chrome_options = Options()
        chrome_options.add_argument("--disable-gpu")
        chrome_options.add_argument("--no-sandbox")
        chrome_options.add_argument("--window-size=1920,1080")
        self.driver = webdriver.Chrome(options=chrome_options)

    def login_zhihu(self):
        def check_login(driver):
            return (
                "login" not in driver.current_url
                and "account" not in driver.current_url
            )

        self.logger.info(f"[{self.class_name}] 检查登录状态...")
        self.ensure_login(self.driver, self.cookie_path, check_login)

    def ensure_login(self, driver, cookie_path, check_func):
        driver.get(self.home_url)  # 加载平台首页，确保 driver 初始化
        self.load_cookies(driver, cookie_path)
        driver.refresh()
        self.sleep_random()
        if self.is_logged_in(driver, check_func):
            self.logger.info(f"[{self.class_name}] 已登录")
        else:
            self.logger.info(f"[{self.class_name}] 未登录，请手动操作登录")
            input(f"[{self.class_name}] 完成登录后按回车继续：")
            self.save_cookies(driver, cookie_path)

    def get_random_comment(self):
        return random.choice(self.comments)

    def run(self, interval=30):
        self.setup_browser()
        self.login_zhihu()
        while True:
            hrefs = self.get_recommended_note_links()
            if not hrefs:
                self.logger.error(f"[{self.class_name}] 未获取到笔记链接，等待重试...")
                self.sleep_random(base=1.0, jitter=10.0)
                continue
            self.logger.info(f"[{self.class_name}] 开始评价...")
            self.comment_on_note_links(hrefs)

            sleep_time = max(1, interval + random.uniform(-1, 3))
            self.logger.info(f"下轮将在 {int(sleep_time)} 秒后继续...")
            self.sleep_random(base=1.0, jitter=sleep_time)

    def comment_on_note_links(self, note_links):
        """
        Visit each note URL, open the comment box, type a random comment, and send.
        Includes robust waits + fallbacks to reduce ChromeDriver crashes caused by stale/absent elements.
        """
        base = "https://www.zhihu.com"
        # --- 读取缓存文件到 current_cache ---
        if os.path.exists(self.cache_path):
            try:
                with open(self.cache_path, "r", encoding="utf-8") as f:
                    current_cache = json.load(f)
            except Exception:
                current_cache = {}
        else:
            current_cache = {}
            
        for idx, (url, title) in enumerate(
            tqdm(note_links.items(), desc="评论进度"), 1
        ):
            # 跳过已评论过的链接
            if self.comment_db.has_commented(url, Platform.ZHIHU):
                self.logger.info(f"[{self.class_name}] 已跳过已评论过的链接：{url}")
                # --- 移除已评论链接 ---
                if url in current_cache:
                    self.remove_cache(url)
                    del current_cache[url]
                continue
            self.logger.info(
                f"[{self.class_name}] 正在评论第 {idx}/{len(note_links)} 条（标题：{title}）..."
            )
            # Normalize URL (some feeds return relative URLs)
            if url and url.startswith("/"):
                url = urllib.parse.urljoin(base, url)

            try:
                self.logger.info(f"[{self.class_name}] 打开笔记：{url}（标题：{title}）")
                self.driver.get(url)

                # 等待文档 ready (body 存在 & readyState == complete)
                WebDriverWait(self.driver, 15).until(
                    EC.presence_of_element_located((By.TAG_NAME, "body"))
                )
                WebDriverWait(self.driver, 15).until(
                    lambda d: d.execute_script("return document.readyState")
                    == "complete"
                )
                self.sleep_random(base=1.0, jitter=1.0)  # 给前端框架一点渲染缓冲

                # 检测是否被踢回登录页（关键词 login 或 qrcode）
                cur = self.driver.current_url
                if "login" in cur or "account" in cur:
                    self.logger.error(f"[{self.class_name}] 页面跳到登录，跳过：{url}")
                    self.exit(1)

                # 等待 DOM 渲染完成，避免多余滚动
                self.sleep_random(base=1.0, jitter=1.0)

                # 定位知乎“评论”触发元素
                trigger_locators = [
                    (
                        By.CSS_SELECTOR,
                        "button.Button.ContentItem-action.Button--withIcon.Button--withLabel",
                    ),
                    (
                        By.CSS_SELECTOR,
                        "button.Button[aria-label*='评论'], button.Button:has(svg.Zi--Comment)",
                    ),
                ]

                comment_trigger = None
                for how, what in trigger_locators:
                    try:
                        comment_trigger = WebDriverWait(self.driver, 10).until(
                            EC.presence_of_element_located((how, what))
                        )
                        WebDriverWait(self.driver, 5).until(
                            EC.element_to_be_clickable((how, what))
                        )
                        break
                    except Exception:
                        continue

                if comment_trigger is None:
                    self.logger.info(f"[{self.class_name}] 未找到评论入口，跳过：{url}")
                    self.failed_comment_count += 1
                    if self.failed_comment_count >= 3:
                        self.logger.info(f"[{self.class_name}] 连续未找到评论入口 3 次，程序退出")
                        self.exit(1)
                    continue

                # 确保在视口中
                try:
                    self.driver.execute_script(
                        "arguments[0].scrollIntoView({block:'center'});",
                        comment_trigger,
                    )
                except Exception:
                    pass
                self.sleep_random(base=1.0, jitter=1.0)

                try:
                    comment_trigger.click()
                except Exception:
                    self.driver.execute_script("arguments[0].click();", comment_trigger)

                # 等评论输入框（Draft.js 编辑器定位）
                input_locators = [
                    (
                        By.CSS_SELECTOR,
                        "div.public-DraftEditor-content[contenteditable='true']",
                    )
                ]

                input_box = None
                for how, what in input_locators:
                    try:
                        input_box = WebDriverWait(self.driver, 10).until(
                            EC.presence_of_element_located((how, what))
                        )
                        # 确保输入框在视口中
                        try:
                            self.driver.execute_script(
                                "arguments[0].scrollIntoView({block:'center'});",
                                input_box,
                            )
                        except Exception:
                            pass
                        self.sleep_random(base=1.0, jitter=1.0)
                        break
                    except Exception:
                        continue

                if input_box is None:
                    self.logger.info(f"[{self.class_name}] 未找到评论输入框，跳过：{url}")
                    self.failed_comment_count += 1
                    if self.failed_comment_count >= 3:
                        self.logger.info(
                            f"[{self.class_name}] 连续未找到评论输入框 3 次，程序退出"
                        )
                        self.exit(1)
                    continue

                # 激活输入框（有些前端需要点击 innerEditable 子节点）
                try:
                    ActionChains(self.driver).move_to_element(
                        input_box
                    ).click().perform()
                except Exception:
                    self.driver.execute_script("arguments[0].click();", input_box)
                self.sleep_random(base=1.0, jitter=1.0)

                comment = self.remove_non_bmp(self.get_random_comment())

                # 有些 contenteditable 节点不吃 send_keys；先试 send_keys，失败则 JS 赋值
                typed_ok = True
                try:
                    input_box.clear()  # 若支持
                except Exception:
                    pass
                try:
                    input_box.send_keys(comment)
                except Exception:
                    typed_ok = False

                if not typed_ok:
                    try:
                        self.driver.execute_script(
                            "arguments[0].innerText = arguments[1]; arguments[0].dispatchEvent(new Event('input',{bubbles:true}));",
                            input_box,
                            comment,
                        )
                        typed_ok = True
                    except Exception:
                        typed_ok = False

                if not typed_ok:
                    self.logger.info(f"[{self.class_name}] 无法输入评论，跳过：{url}")
                    self.failed_comment_count += 1
                    if self.failed_comment_count >= 3:
                        self.logger.info(f"[{self.class_name}] 连续无法输入评论 3 次，程序退出")
                        self.exit(1)
                    continue

                # 发送评论按钮（新版样式）
                send_locators = [
                    (By.CSS_SELECTOR, "button.Button--primary.Button--blue")
                ]

                submit_button = None
                for how, what in send_locators:
                    try:
                        submit_button = WebDriverWait(self.driver, 10).until(
                            EC.element_to_be_clickable((how, what))
                        )
                        break
                    except Exception:
                        continue

                if submit_button is None:
                    self.logger.info(f"[{self.class_name}] 找不到发送按钮，跳过：{url}")
                    self.failed_comment_count += 1
                    if self.failed_comment_count >= 3:
                        self.logger.info(f"[{self.class_name}] 连续找不到发送按钮 3 次，程序退出")
                        self.exit(1)
                    continue

                try:
                    submit_button.click()
                except Exception:
                    self.driver.execute_script("arguments[0].click();", submit_button)

                # 检测 toast：失败 OR 成功
                toast_failed = False
                try:
                    # 等待任一 toast 出现
                    toast = WebDriverWait(self.driver, 5).until(
                        EC.presence_of_element_located(
                            (
                                By.XPATH,
                                "//*[contains(@class,'toast') or contains(@class,'Toast')]",
                            )
                        )
                    )
                    toast_text = toast.text.strip()
                    if any(k in toast_text for k in ["操作频繁", "失败", "请稍后"]):
                        toast_failed = True
                except Exception:
                    # 无 toast，假设成功
                    pass

                if toast_failed:
                    self.logger.info(f"[{self.class_name}] 评论失败（toast）：{url}")
                    self.failed_comment_count += 1
                    if self.failed_comment_count >= 3:
                        self.logger.info(f"[{self.class_name}] 连续失败 3 次，程序退出")
                        self.exit(1)
                    continue  # 下一条

                # 成功
                self.logger.info(f"[{self.class_name}] 已评论 {comment}   链接：{url}")
                # 记录已评论
                self.comment_db.record_comment(
                    platform=Platform.ZHIHU,
                    url=url,
                    title=title,
                    comment=comment,
                    status="success",
                )
                self.comment_count += 1
                self.failed_comment_count = 0
                self.comment_count_data[self.today] = self.comment_count
                with open(self.comment_count_path, "w", encoding="utf-8") as f:
                    json.dump(self.comment_count_data, f, ensure_ascii=False, indent=2)
                self.count_logger.info(f"{self.today} 累计评论：{self.comment_count}")

                if self.comment_count >= limitation:
                    self.logger.info(
                        f"[{self.class_name}] 今日评论已达 {limitation} 条，程序退出"
                    )
                    self.exit(0)

                self.sleep_random(base=1.0, jitter=1.0)

            except Exception as e:
                self.logger.info(f"[{self.class_name}] 评论链接失败: {url}，错误：{e}")
                self.failed_comment_count += 1
                if self.failed_comment_count >= 3:
                    self.logger.info(f"[{self.class_name}] 连续失败 3 次，程序退出")
                    self.exit(1)
    

    def get_recommended_note_links(self, scroll_times: int = 5):
        """
        Collect note links from the recommend feed.
        Scroll multiple times to load more.
        Returns a dict: {url: title}
        """
        self.logger.info(f"[{self.class_name}] get_recommended_note_links...")
        
        self.driver.get("https://www.zhihu.com/hot")

        # 等初次加载
        WebDriverWait(self.driver, 15).until(
            EC.presence_of_element_located((By.TAG_NAME, "body"))
        )
        self.sleep_random(base=1.0, jitter=1.0)

        hrefs = dict()
        last_height = 0
        for i in range(scroll_times):
            sections = self.driver.find_elements(
                By.CSS_SELECTOR, "div.HotList-list section.HotItem"
            )
            for section in sections:
                try:
                    a_tag = section.find_element(By.CSS_SELECTOR, "a[title]")
                    href = a_tag.get_attribute("href") or ""
                    title = a_tag.get_attribute("title") or ""
                    if href and title:
                        if self.comment_db.has_commented(href, Platform.ZHIHU):
                            continue
                        hrefs[href] = title
                        self.logger.info(f"[{self.class_name}] 抓取链接: {href} 标题: {title}")
                except Exception:
                    continue
            # 滚动到底部加载更多
            self.driver.execute_script(
                "window.scrollBy(0, document.body.scrollHeight);"
            )
            self.sleep_random(base=1.0, jitter=1.0)

            # 可选：检测页面高度变化（粗略）
            try:
                new_height = self.driver.execute_script(
                    "return document.body.scrollHeight;"
                )
                if new_height == last_height:
                    # 没有新内容，提前退出
                    break
                last_height = new_height
            except Exception:
                pass

        self.logger.info(f"[{self.class_name}] 共获取到 {len(hrefs)} 条链接，其中包含标题信息")
        return hrefs


if __name__ == "__main__":
    print("[ZhihuBot] started...")
    bot = ZhihuBot()
    bot.run()
    print("[ZhihuBot] ended...")
